﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Threading.Tasks;
using E4D.AppFabric.Entities;
using Microsoft.ApplicationServer.Caching;
using Microsoft.ApplicationServer.Caching.AdminApi;
using E4D.AppFabric.Entities.Cache_Host;

namespace MDCAdminTool
{
    public enum Eviction
    {
        None ,
        LRU
    }

    public class MdcCache : PowerShellBase<CacheHost, CacheRegion> 
    {
        #region Fields
        private bool            _IsHighAvailability;
        private Eviction        _Eviction;
        private long            _TTL;
        private bool            _Expirable;
        private bool            _NotificationsEnabled;
        private NamedCacheStats _NamedCacheStats;
        private string          _CacheType;
        private Collection<CacheRegion> _CacheRegions;
        private NotificationOperations _notificationOperations;
        #endregion

        #region Properties
        public string CacheType
        {
            get { return _CacheType; }
            set { _CacheType = value; OnPropertyChanged( "CacheType" ); }
        }
        public bool IsHighAvailability
        {
            get { return _IsHighAvailability; }
            set { _IsHighAvailability = value; OnPropertyChanged( "IsHighAvailability" ); }
        }
        public Eviction Eviction
        {
            get { return _Eviction; }
            set { _Eviction = value; OnPropertyChanged( "Eviction" ); }
        }
        public long TTL
        {
            get { return _TTL; }
            set { _TTL = value; OnPropertyChanged( "TTL" ); }
        }
        public bool IsExpirable
        {
            get { return _Expirable; }
            set { _Expirable = value; OnPropertyChanged( "Expirable" ); }
        }
        public Dictionary<string , string[]> HostRegionMap { set; get; }
        public NamedCacheStats NamedCacheStats
        {
            get { return _NamedCacheStats; }
            set { _NamedCacheStats = value; OnPropertyChanged( "NamedCacheStats" ); }
        }
        public bool NotificationsEnabled
        {
            get { return _NotificationsEnabled; }
            set { _NotificationsEnabled = value; OnPropertyChanged( "NotificationsEnabled" ); }
        }
        public Collection<CacheRegion> CacheRegions
        {
            get { return _CacheRegions; }
            set { _CacheRegions = value; OnPropertyChanged( "CacheRegions" ); }
        }
        public NotificationOperations NotificationOperations
        {
            get { return _notificationOperations; }
            set
            {
                _notificationOperations = value; 
                OnPropertyChanged("NotificationOperations");
            }
        }
        #endregion

        #region Constructors
        public MdcCache( string name )
        {
            Type          = MDCAdminTool.CacheType.CacheName;
            Name          = name;
            CacheRegions  = new Collection<CacheRegion>();
            Params        = new Dictionary<string , object>{ {"CacheName"  , Name} };

        }

        public MdcCache( CacheHost host , string name ) : this(name)
        {
            //IsHighAvailability  = false;
            //Eviction            = Eviction.LRU;
            //TTL                 = 10;
            //NotExpirable        = true;

            Parent = host;
            Output = host.Output;

            new TaskFactory()
                .StartNew( LoadRegions )
                .ContinueWith( ( t ) => LoadConfig() );
            

            //LoadConfig();
            //LoadRegions();

        }
        public MdcCache( CacheHost host , CacheInfo cacheInfo )
            : this( host , cacheInfo.CacheName )
        {

        }
        public MdcCache( CacheHost host , string name , bool notificationsEnabled , bool isHighAvailability , Eviction eviction , int Ttl , bool isExpirable )
            : this( host , name )
        {
            IsHighAvailability  = isHighAvailability;
            Eviction            = eviction;
            TTL                 = Ttl;
            IsExpirable        = !isExpirable;
            NotificationsEnabled = notificationsEnabled;
        }
        #endregion

        #region PowerShell Commands

        /// <summary>
        /// Returns a list of all regions in the cache cluster or only those in a named cache.
        /// </summary>
        /// <returns>Returns a list of all regions in the cache cluster or only those in a named cache.</returns>
        public void LoadRegions()
        {
            var r = Invoke("Get-CacheRegion", Params);
            CacheRegions.Clear();

            if (r == null || r.Count == 0) return; 

            var result = GetRegionResult(r);

            if (result == null || result.Count <= 0) return;

            foreach (var cr in result)
            {
                //if( !cr.Name.Contains("Default_Region") )
                    CacheRegions.Add(cr);
            }

            OnPropertyChanged( "CacheRegions" );
        }

        private Collection<CacheRegion> GetRegionResult(IEnumerable<PSObject> r)
        {
            var result = new Collection<CacheRegion>();

            foreach (var region in r)
            {
                result.Add( new CacheRegion
                                (
                                   (string)region.Properties["RegionName"].Value,
                                   (string)region.Properties["Role"].Value ,
                                   (int)region.Properties["PortNo"].Value ,
                                   this
                                ) );
            }
            return result;
        }

        /// <summary>
        /// Add Region to cache name.
        /// </summary>
        /// <param name="region">Region Name</param>
        /// <param name="evictionOn">Eviction On</param>
        public void AddRegion( string region , bool evictionOn )
        {
            try
            {
                var client = CacheClientFactory.GetCache(this);

                if ( client != null )
                {
                    Log( new PowerShellCommand { CommandName = string.Format(@"DataCache.CreateRegion( {0} )", region )} );
                    client.CreateRegion(region);
                }
            }
            catch ( Exception ex)
            {
                Log( new PowerShellCommand { CommandName = ex.Message } );
            }
            LoadRegions();
        }

        /// <summary>
        /// Load statistics of a named cache
        /// </summary>
        public override CacheStats LoadStatistics()
        {
            var r = Invoke( "Get-CacheStatistics" , Params );
            Collection<NamedCacheStats> result = r.BuildNamedCacheStats();

            if ( result.Count > 0 )
                NamedCacheStats = result[0];
            return NamedCacheStats;
        }

        /// <summary>
        /// Returns the configuration details for the named cache
        /// </summary>
        /// <returns>Returns the configuration details for the named cache</returns>
        public override void LoadConfig()
        {
            try
            {
                var r = Invoke( "Get-CacheConfig" , Params );
                if ( r != null && r.Count > 0 )
                {
                    var result = r[0];
                    if ( result != null )
                    {
                        Name                 = Convert.ToString( result.Properties["CacheName"].Value );
                        Eviction             = (Eviction)Enum.Parse( typeof( Eviction ) , result.Properties["EvictionType"].Value.ToString() );
                        IsExpirable          = Convert.ToBoolean( result.Properties["IsExpirable"].Value );
                        NotificationsEnabled = Convert.ToBoolean( result.Properties["NotificationsEnabled"].Value );
                        TTL                  = Convert.ToInt64( result.Properties["TimeToLive"].Value );
                        IsHighAvailability   = Convert.ToBoolean( result.Properties["Secondaries"].Value );
                        CacheType            = Convert.ToString( result.Properties["CacheType"].Value );
                    }
                }
            }
            catch ( Exception ex )
            {
                AddResultToLog( ex.Message );
            }
        }

        /// <summary>
        /// Updates the configuration settings of the named cache
        /// </summary>
        public override void SaveConfig()
        {
            var p = new Dictionary< string , object >
                        {
                            {"CacheName"    , Name},
                            {"Secondaries"  , IsHighAvailability ? 1 : 0 },
                            {"TimeToLive"   , TTL},
                            {"NotificationsEnabled"  , NotificationsEnabled},
                            {"Eviction"     , Eviction},
                            {"Expirable"    , IsExpirable}
                        };

            var r = Invoke( "Set-CacheConfig" , p );
        }

        public override void Add( CacheEntityBase item )
        {
            if ( item is CacheRegion ) Add(item as CacheRegion);
        }
        public override void Add( CacheRegion region )
        {
           AddRegion( region.Name , region.EvictionOn);
        }
        public override void Remove( CacheRegion region )
        {
            Remove( region as CacheEntityBase );
        }
        public override void Remove( CacheEntityBase item )
        {
            try
            {
                Log( "DataCliet.RemoveRegion(...)" );
                var dc = CacheClientFactory.GetCache( this );
                if ( dc == null ) return;

                dc.RemoveRegion( item.Name );
            }
            catch ( Exception ex )
            {
                Log( new PowerShellCommand { CommandName = ex.Message } );
            }
            LoadRegions();
        }
        #endregion

        #region Not Implemented
        public override void Start()
        {
            throw new NotImplementedException();
        }
        public override void Stop()
        {
            throw new NotImplementedException();
        }
        public override void LoadChildren()
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
